/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2009，所有权利保留。
 * 
 * 项目名：	rumba-widget2
 * 文件名：	RDateTimeBox.java
 * 模块说明：	
 * 修改历史：
 * Feb 13, 2009 - lxm - 创建。
 */
package com.hd123.rumba.gwt.widget2.client.form;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.gwt.dom.client.Document;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.ListenerWrapper;
import com.google.gwt.user.client.ui.SourcesChangeEvents;
import com.google.gwt.user.client.ui.Widget;
import com.hd123.rumba.gwt.util.client.RDateUtil;
import com.hd123.rumba.gwt.widget2.client.fielddef.DateFieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.FieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.HasFieldDef;
import com.hd123.rumba.gwt.widget2.client.form.RValidator.ValidResult;
import com.hd123.rumba.gwt.widget2.client.panel.FlowPanelHelper;
import com.hd123.rumba.gwt.widget2.client.tip.RTip;

/**
 * 日期+时间输入框。
 * <p>
 * 
 * <b>控件结构：</b> FlowPanel class=rb-DateTimeBox<br>
 * ..RDateBox<br>
 * ..RTimeBox<br>
 * ..div class=note
 * <p>
 * 
 * <b>CSS Style Rules</b> <ol type=disc>
 * <li>.rb-DateTimeBox {}</li>
 * <li>.rb-DateTimeBox .note {}</li> </ol>
 * 
 * @author lxm
 * 
 */
@SuppressWarnings("deprecation")
public class RDateTimeBox extends RFieldComposite implements SourcesChangeEvents,
    HasChangeHandlers, HasRValidator, HasFieldDef {

  private static final String STYLENAME_DEFAULT = "rb-DateTimeBox";

  /**
   * 创建对象实例。
   */
  public RDateTimeBox() {
    super();
    drawSelf();
  }

  /**
   * @param fieldDef
   *          字段定义
   */
  public RDateTimeBox(FieldDef fieldDef) {
    this();
    setFieldDef(fieldDef);
  }

  private FlowPanel pnl;
  private Helper_pnl helper;
  private RDateBox dateBox;
  private RTimeBox timeBox;

  private static final String CNAME_DATE = "date";
  private static final String CNAME_TIME = "time";
  private static final String CNAME_NOTE = "note";

  private RTip tip;
  private RValidator validator;
  private FieldDef fieldDef;

  /**
   * 取得当前取值。
   * 
   * @return
   */
  public Date getValue() {
    Date date = dateBox.getValue();
    if (date == null)
      return null;
    Date time = timeBox.getValue();
    if (time == null)
      return date;
    return RDateUtil.assemble(date, time);
  }

  /**
   * 设置当前取值。
   * 
   * @param value
   */
  public void setValue(Date value) {
    if (value == null) {
      dateBox.setValue(null);
      timeBox.setValue(null);
    } else {
      dateBox.setValue(value);
      timeBox.setValue(value);
    }
  }

  /**
   * 清空取值。清空后getValue()将返回null。
   */
  public void clearValue() {
    setValue(null);
  }

  /**
   * 取得所包含的日期输入框对象。
   * 
   * @return
   */
  public RDateBox getDateBox() {
    return dateBox;
  }

  /**
   * 取得所包含的时间输入框对象。
   * 
   * @return
   */
  public RTimeBox getTimeBox() {
    return timeBox;
  }

  /**
   * 取得说明。
   * 
   * @return
   */
  public String getNote() {
    if (!helper.isCreated(CNAME_NOTE))
      return null;
    HTML html = (HTML) helper.getChild(CNAME_NOTE);
    return html.getHTML();
  }

  /**
   * 返回说明。
   * 
   * @param note
   */
  public void setNote(String note) {
    HTML html = (HTML) helper.getChild(CNAME_NOTE);
    html.setHTML(note == null ? "" : note);
  }

  /**
   * 取得后缀。
   * 
   * @return
   */
  public String getSuffix() {
    return timeBox.getSuffix();
  }

  /**
   * 设置后缀。
   * 
   * @param suffix
   */
  public void setSuffix(String suffix) {
    timeBox.setSuffix(suffix);
  }

  public void setEnabled(boolean enabled) {
    super.setEnabled(enabled);
    dateBox.setEnabled(enabled);
    timeBox.setEnabled(enabled);
  }

  public void setRequired(boolean required) {
    super.setRequired(required);
    if (fieldDef != null)
      fieldDef.setNullable(!required);
  }

  /**
   * 取得是否只读。
   * 
   * @return
   */
  public boolean isReadOnly() {
    return dateBox.isReadOnly() && timeBox.isReadOnly();
  }

  /**
   * 设置是否只读。
   * 
   * @param readOnly
   */
  public void setReadOnly(boolean readOnly) {
    dateBox.setReadOnly(readOnly);
    timeBox.setReadOnly(readOnly);
  }

  /**
   * 设置是否获得焦点。
   * 
   * @param focused
   */
  public void setFocus(boolean focused) {
    dateBox.setFocus(focused);
  }

  /**
   * @deprecated 请用{@link #addChangeHandler}代替
   */
  @Deprecated
  public void addChangeListener(ChangeListener listener) {
    ListenerWrapper.WrappedChangeListener.add(this, listener);
  }

  /**
   * @deprecated 请用{@link #addChangeHandler}返回的
   *             {@link HandlerRegistration#removeHandler}代替
   */
  @Deprecated
  public void removeChangeListener(ChangeListener listener) {
    ListenerWrapper.WrappedChangeListener.remove(this, listener);
  }

  public HandlerRegistration addChangeHandler(ChangeHandler handler) {
    return addDomHandler(handler, ChangeEvent.getType());
  }

  public RTip getTip() {
    return tip;
  }

  /**
   * 日期格式。默认为日期框的格式加上时间框的格式，中间用一个空格分隔。
   */
  public DateTimeFormat getFormat() {
    return DateTimeFormat.getFormat(dateBox.getFormat().getPattern() + " "
        + timeBox.getFormat().getPattern());
  }

  /**
   * 设置日期格式。传入的日期格式将被拆分日期部分和时间部分分别赋予日期框和时间框，这要求传入的格式为
   * 日期格式加上时间格式，中间用一个空格分隔。如果不符合这种格式将会抛出异常。
   * 
   * @param format
   */
  public void setFormat(DateTimeFormat format) {
    if (format == null)
      throw new NullPointerException("传入的格式不能为空");

    String[] patterns = format.getPattern().split(" ");
    dateBox.setFormat(DateTimeFormat.getFormat(patterns[0]));
    timeBox.setFormat(DateTimeFormat.getFormat(patterns[1]));
  }

  public RValidator getValidator() {
    return validator;
  }

  public void setValidator(RValidator validator) {
    this.validator = validator;
  }

  public void clearValidResults() {
    dateBox.clearValidResults();
    timeBox.clearValidResults();
    tip.clear();
  }

  public boolean isValid() {
    return dateBox.isValid() && timeBox.isValid() && tip.isValid();
  }

  public List getInvalidMessages() {
    List list = new ArrayList();
    if (!isValid())
      list.add(getTip().getDisplayed().toMessageRecord(dateBox));
    return list;
  }

  public boolean validate() {
    if (isReadOnly() || !isEnabled() || !isVisible())
      return true;

    dateBox.validate();
    timeBox.validate();

    getTip().clear();

    if (fieldDef == null && isRequired() && dateBox.isEmpty() && timeBox.isEmpty()) {
      getTip().addError((getFieldCaption() == null ? "" : getFieldCaption() + ":") + "不可以为空。");
    }

    if (validator != null) {
      Date value = getValue();
      ValidResult validResult = validator.validate(this, value == null ? "" : getFormat().format(
          getValue()));
      getTip().add(validResult);
      dateBox.getTip().add(validResult);
      timeBox.getTip().add(validResult);
    }

    return isValid();
  }

  public FieldDef getFieldDef() {
    return fieldDef;
  }

  public void setFieldDef(FieldDef fieldDef) {
    this.fieldDef = fieldDef == null ? null : fieldDef.clone();
    if (this.fieldDef == null)
      return;

    setFieldCaption(this.fieldDef.getCaption());
    setRequired(this.fieldDef.isRequired());
    setValidator(new RValidatorOnFieldDef(this.fieldDef));

    if (fieldDef instanceof DateFieldDef) {
      setFormat(((DateFieldDef) fieldDef).getFormat());
    }
  }

  private void drawSelf() {
    pnl = new FlowPanel();

    helper = new Helper_pnl(pnl);
    helper.appendChild(CNAME_DATE, false);
    helper.appendChild(CNAME_TIME, false);
    helper.appendChild(CNAME_NOTE, true);

    tip = new RTip();
    timeBox.getTip().getButton().bind(tip);
    timeBox.getTip().getButton().bind(dateBox.getTip());

    ChangeHandler changeHandler = new ChangeHandler() {
      public void onChange(ChangeEvent event) {
        if (event.getSource().equals(dateBox))
          timeBox.validate();
        else if (event.getSource().equals(timeBox))
          dateBox.validate();

        if (dateBox.isValid() && timeBox.isValid()) {
          validate();
          if (isValid())
            DomEvent.fireNativeEvent(Document.get().createChangeEvent(), RDateTimeBox.this);
        }
        event.stopPropagation();
      }
    };
    dateBox.addChangeHandler(changeHandler);
    timeBox.addChangeHandler(changeHandler);

    // TODO
    initWidget(pnl);
    setStyleName(STYLENAME_DEFAULT);
  }

  private class Helper_pnl extends FlowPanelHelper {

    public Helper_pnl(FlowPanel parent) {
      super(parent);
    }

    protected Widget createChild(String childName) {
      if (CNAME_DATE.equals(childName)) {
        dateBox = new RDateBox();
        dateBox.setInputWidth("70px");
        return dateBox;
      }

      if (CNAME_TIME.equals(childName)) {
        timeBox = new RTimeBox();
        timeBox.setInputWidth("58px");
        return timeBox;
      }

      if (CNAME_NOTE.equals(childName)) {
        HTML html = new HTML();
        html.setStyleName("note");
        return html;
      }
      return null;
    }

  }

}
